<?php

/**
 * Principal property
 *
 * The principal property represents a principal from RFC3744 (ACL).
 * The property can be used to specify a principal or pseudo principals.
 *
   * Copyright © 2010 Jack Cleaver
   * 
   * This plugin provides functionality added by CalDAV (RFC 4791)
   * It implements new reports, and the MKCALENDAR method.
   *
   * This file is part of Slash.
   *
   * Slash is free software: you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation, either version 3 of the License, or
   * (at your option) any later version.
   *
   * Slash is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with Slash.  If not, see <http://www.gnu.org/licenses/>.
 */
class Slash_ACL_Property_PropertyPrincipal 
extends Slash_ACL_Property_Principal
{

	/**
	 * Property name for principal
	 *
	 * @var string
	 */
	private $uri;
	private $propName;

	/**
	 * Creates the property.
	 * @param string $uri
	 * The XML namespace URI for the referenced property
	 * 
	 * @param string $propName
	 * The XML local name of the property.
	 *
	 */
	public function __construct($uri, $propName)
	{
		Slash_Debug::debug("ACL_Property_PropertyPrincipal constructor: $uri $propName");
		$this->uri = $uri;
		$this->propName = $propName;
	}
	
	public static function fromString($princStr)
	{
		Slash_Debug::debug("ACL_Property_PropertyPrincipal fromString: $princStr");
		if (preg_match("/[^:]+:{([^}]+)}(.+)/", $princStr, $matches))
		{
			return new self($matches[1], $matches[2]);
		}
		throw new Sabre_DAV_Exception("Invalid PropertyPrincipal string: $princStr");
	}
	
	public function toString()
	{
		return "property:{" . $this->uri . "}" . $this->propName;
	}


	/*
	 * Get the href value of the property named by this ProperyPrincipal
	 * from the referenced node.
	 * @param Sabre_DAV_FSExt_Node $resourceNode
	 * @return string
	 */  
	public function getHrefForNode(Sabre_DAV_FSExt_Node $resourceNode)
	{
		/*
		 * A property principal in the ACL for a resource matches an href principal if the
		 * resource has the identified property, the property has one href, and that
		 * href identifies the href principal.
		 * SO:
		 * - get the current resource
		 * - get the desired property on that resource
		 * - get the href on that property
		 * - get the current user principal (resource)
		 * - get the uri of that user
		 * - compare the uri with the href
		 *
		 */
		$result = false;
		Slash_Debug::debug("PropertyPrincipal getHref: this=" . $this->toString());
		$propName = $this->getPropName();
		$propreq = array($propName);
		$props = $resourceNode->getProperties($propreq);
		if (isset($props[$propName]))
		{
			Slash_Debug::debug("PropertyPrincipal getHref: node has property $propName");
			$prop = $props[$propName];
			if ($prop instanceof Sabre_DAV_Property_IHref)
			{
				$result = $prop->getHref();
			}
		}
		return $result;
	}
	
	public function includesPrincipal(Slash_ACL_Property_Principal $princ, Sabre_DAV_FSExt_Node $resourceNode)
	{
		/*
		 * A property principal in the ACL for a resource matches an href principal if the
		 * resource has the identified property, the property has one href, and that
		 * href identifies the href principal.
		 * SO:
		 * - get the current resource
		 * - get the desired property on that resource
		 * - get the href on that property
		 * - get the current user principal (resource)
		 * - get the uri of that user
		 * - compare the uri with the href
		 *
		 */
		$result = false;
		Slash_Debug::debug("PropertyPrincipal includesPrincipal: this=" . $this->toString() .", target=" . $princ->toString());
		if ($princ instanceof Slash_ACL_Property_HrefPrincipal)
		{
			$href = $princ->getHref();
			$propName = $this->getPropName();
			$propreq = array($propName);
			$props = $resourceNode->getProperties($propreq);
			if (isset($props[$propName]))
			{
				Slash_Debug::debug("PropertyPrincipal includesPrincipal: node has property $propName");
				$prop = $props[$propName];
				if ($prop instanceof Sabre_DAV_Property_IHref)
				{
					$propHref = $prop->getHref();
					Slash_Debug::debug("PropertyPrincipal includesPrincipal: property on node is an href: " . $propHref);
					if ($propHref == $href)
					{
						$result = true;
					} else if ($prop instanceof Slash_ACL_Property_Owner)
					{
						$ownerPrincipal = new Slash_ACL_Property_HrefPrincipal($propHref);
						Slash_Debug::debug("PropertyPrincipal includesPrincipal: see if $propHref includes $href");
						$result = $ownerPrincipal->includesPrincipal($princ, $resourceNode);
					}
				}
			} else {
				Slash_Debug::debug("PropertyPrincipal includesPrincipal: node DOES NOT have property $propName");
			}
		}
		return $result;
	}

	/**
	 * Returns the property as a clarked string.
	 *
	 * @return string
	 */
	public function getPropName()
	{
		return "{" . $this->uri . "}" . $this->propName;
	}

	/*
	 * Anomalously, this property unserializes from a property, not a principal.
	 */
	static function unserialize(DOMElement $property)
	{
		Slash_Debug::debug("ACL Property PropertyPrincipal unserialize: property localname=" . $property->localName . ", tagname=".$property->tagName);
		$uri = $property->namespaceURI;
		if ($uri == "urn:DAV")
		{
			$uri = "DAV:";
		}
		return new self($uri, $property->localName);
	}

	/*
	 * Serializing, we serialize onto a principal node. No anomaly.
	 */
	public function serialize(Sabre_DAV_Server $server, DOMElement $princnode)
	{
		$davPrefix = $server->xmlNamespaces['DAV:'];
		$doc = $princnode->ownerDocument;
		$propElement = $doc->createElement("$davPrefix:property");
		$prop = $doc->createElementNS($this->uri, "x:$this->propName");
		$propElement->appendChild($prop);
		$princnode->appendChild($propElement);
	}
}
